package com.yanti.service.impl;

import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.base.Preconditions;
import com.yanti.common.constant.RedisConstants;
import com.yanti.common.constant.RoleConstants;
import com.yanti.domain.ResponseResult;
import com.yanti.domain.dto.user.UpdatePasswordReqDTO;
import com.yanti.domain.dto.user.UpdateUserInfoReqDTO;
import com.yanti.domain.dto.user.UserLoginReqDTO;
import com.yanti.domain.entity.Role;
import com.yanti.domain.entity.User;
import com.yanti.domain.entity.UserRoleRel;
import com.yanti.domain.enums.*;
import com.yanti.domain.exception.BizException;
import com.yanti.domain.vo.UserInfoVO;
import com.yanti.filter.LoginUserContextHolder;
import com.yanti.mapper.UserMapper;
import com.yanti.mapper.UserRoleRelMapper;
import com.yanti.strategy.FileStrategy;
import com.yanti.util.BeanCopyUtils;
import com.yanti.util.ParamUtils;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.Lists;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import com.yanti.service.UserService;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.web.multipart.MultipartFile;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * 用户表(User)表服务实现类
 *
 * @author luo
 * @since 2025-04-09 20:59:33
 */
@Service("userService")
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    @Resource
    private UserMapper userMapper;

    @Resource
    private UserRoleRelMapper userRoleRelmapper;
    @Resource
    private RedisTemplate<String,Object> redisTemplate;

    @Resource
    private TransactionTemplate transactionTemplate;

    @Resource
    private PasswordEncoder passwordEncoder;

    @Resource(name = "taskExecutor")
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;

    @Resource
    private FileStrategy fileStrategy;

    private static final String BUCKET_NAME = "yanti";

    /**
     * 登录注册
     * @param userLoginReqDTO 登录注册请求体
     * @return
     */
    @Override
    public ResponseResult<?> loginAndRegister(UserLoginReqDTO userLoginReqDTO) {
        String phone = userLoginReqDTO.getPhone();
        Integer type = userLoginReqDTO.getType();
        LoginTypeEnum loginTypeEnum = LoginTypeEnum.valueOf(type);
        // 登录类型错误
        if (Objects.isNull(loginTypeEnum)) {
            throw new BizException(AppHttpCodeEnum.LOGIN_TYPE_ERROR);
        }
        // 初始化登录用户ID
        Long userId = null;
        // 判断登录类型
        switch (loginTypeEnum) {
            case VERIFICATION_CODE -> {
                String verificationCode = userLoginReqDTO.getCode();
                // 验证码登录
                // 校验入参验证码是否为空？
                Preconditions.checkArgument(StringUtils.isNotBlank(verificationCode), "验证码不能为空");
//                    if (Objects.isNull(verificationCode)) {
//                        // 验证码不能为空
//                        throw new BizException(AppHttpCodeEnum.VERIFICATION_CODE_NOT_NULL);
//                    }
                String key = RedisConstants.buildVerificationCodeKey(phone);
                // 从 redis 中获取验证码
                String code = (String) redisTemplate.opsForValue().get(key);
                // 验证验证码是否正确
                if (!StringUtils.equals(code, verificationCode)) {
                    // 验证码错误
                    throw new BizException(AppHttpCodeEnum.VERIFICATION_CODE_ERROR);
                }
                // 判断用户是否注册
                User user = userMapper.selectByPhone(phone);
                log.info("==> 用户是否注册, phone: {}, user: {}", phone, JSONUtil.toJsonStr(user));
                if (Objects.isNull(user)) {
                    // 注册用户
                    userId = register(phone);
                } else {
                    userId = user.getId();
                }
            }
            case PASSWORD -> {
                // 密码格式校验，长度在6-16位之间，且只能包含数字和字母或者是特殊字符
                Preconditions.checkArgument(ParamUtils.checkPassword(userLoginReqDTO.getPassword()),  AppHttpCodeEnum.PASSWORD_ERROR.getErrorMessage());
                boolean checked = ParamUtils.checkPassword(userLoginReqDTO.getPassword());
                // 密码登录
                String password = userLoginReqDTO.getPassword();
                // 根据手机号查询用户信息
                User user2 = userMapper.selectByPhone(phone);
                // 判断该手机号是否注册
                if (Objects.isNull(user2)) {
                    throw new BizException(AppHttpCodeEnum.USER_NOT_EXIST);
                }
                // 拿到密文密码
                String encodePassword = user2.getPassword();
                // 校验密码是否正确
                if (!passwordEncoder.matches(password, encodePassword)) {
                    throw new BizException(AppHttpCodeEnum.PHONE_OR_PASSWORD_ERROR);
                }
                userId = user2.getId();
            }
            default -> {
                return ResponseResult.errorResult(500, "登录类型异常");
            }
        }
        // SaToken 登录用户，并返回 token 令牌
        StpUtil.login(userId);
        // 获取Token令牌
        SaTokenInfo tokenInfo = StpUtil.getTokenInfo();
        // 构建返回结果
        return ResponseResult.okResult(tokenInfo.tokenValue);
    }

    @Override
    public ResponseResult<?> logout() {
        // 获取当前登录用户的 ID
        Long userId = LoginUserContextHolder.getUserId();
        log.info("==> 用户退出登录, userId: {}", userId);

        threadPoolTaskExecutor.submit(() -> {
            Long userId2 = LoginUserContextHolder.getUserId();
            log.info("==> 异步线程中获取 userId: {}", userId2);
        });
        // 退出登录 (指定用户 ID)
        StpUtil.logout(userId);
        return ResponseResult.okResult();
    }

    @Override
    public ResponseResult<?> updatePassword(UpdatePasswordReqDTO updatePasswordReqDTO) {
        // 校验入参
        String newPassword = updatePasswordReqDTO.getNewPassword();
        // 密码加密
        String encodePassword = passwordEncoder.encode(newPassword);
        // 获取当前登录用户的 ID
        Long userId = LoginUserContextHolder.getUserId();
        // 根据用户 ID 查询用户信息
        User user = userMapper.selectById(userId);
        // 校验用户是否存在
        if (Objects.isNull(user)) {
            // 用户不存在
            throw new BizException(AppHttpCodeEnum.USER_NOT_EXIST);
        }
        // 构建用户信息
        User userInfo = User.builder()
                // 用户 ID
                .id(userId)
                // 新密码
                .password(encodePassword)
                // 更新时间
                .updateTime(LocalDateTime.now())
                .build();
        // 更新用户信息
        int rows = userMapper.updateById(userInfo);
        if (rows > 0) {
            return ResponseResult.okResult();
        }else{
            return ResponseResult.errorResult(AppHttpCodeEnum.SYSTEM_ERROR);
        }
    }

    @Override
    public ResponseResult<?> updateUserInfo(UpdateUserInfoReqDTO updateUserInfoReqDTO) {
        User user = new User();
        // 设置当前需要更新的用户ID
        user.setId(LoginUserContextHolder.getUserId());
        // 标识为：是否需要更新
        boolean needUpdate = false;
        // 头像
        String avatarFileUrl = updateUserInfoReqDTO.getAvatar();
        if(Objects.nonNull(avatarFileUrl)){
            // todo 调用对象存储服务上传文件
            user.setAvatar(avatarFileUrl);
            needUpdate = true;
        }

        // 昵称
        String nickname = updateUserInfoReqDTO.getNickname();
        if (StringUtils.isNotBlank(nickname)) {
            Preconditions.checkArgument(ParamUtils.checkNickname(nickname), AppHttpCodeEnum.NICK_NAME_VALID_FAIL.getErrorMessage());
            user.setNickname(nickname);
            needUpdate = true;
        }

        // 研题号
        String yantiId = updateUserInfoReqDTO.getYantiId();
        if (StringUtils.isNotBlank(yantiId)) {
            Preconditions.checkArgument(ParamUtils.checkYanTiId(yantiId), AppHttpCodeEnum.YANTI_ID_VALID_FAIL.getErrorMessage());
            // 判断研题号是否已存在（排除当前用户自身的研题号）
            Long currentUserId = LoginUserContextHolder.getUserId();
            if (null!= userMapper.selectByYanTiIdAndExcludeCurrentUser(yantiId, currentUserId)) {
                throw new BizException(AppHttpCodeEnum.YANTI_ID_VALID_FAIL);
            }
            user.setYantiId(yantiId);
            needUpdate = true;
        }

        // 性别
        Integer sex = updateUserInfoReqDTO.getSex();
        if (Objects.nonNull(sex)) {
            Preconditions.checkArgument(SexEnum.isValid(sex), AppHttpCodeEnum.SEX_VALID_FAIL.getErrorMessage());
            user.setSex(sex);
            needUpdate = true;
        }

        // 生日
        LocalDate birthday = updateUserInfoReqDTO.getBirthday();
        if (Objects.nonNull(birthday)) {
            user.setBirthday(birthday);
            needUpdate = true;
        }

        // 个人简介
        String introduction = updateUserInfoReqDTO.getIntroduction();
        if (StringUtils.isNotBlank(introduction)) {
            Preconditions.checkArgument(ParamUtils.checkLength(introduction, 100), AppHttpCodeEnum.INTRODUCTION_VALID_FAIL.getErrorMessage());
            user.setIntroduction(introduction);
            needUpdate = true;
        }

        // 背景图
        String backgroundImgUrl = updateUserInfoReqDTO.getBackgroundImg();
        if (Objects.nonNull(backgroundImgUrl)) {
            // todo: 调用对象存储服务上传文件
            user.setBackgroundImg(backgroundImgUrl);
            needUpdate = true;
        }
        if (needUpdate) {
            // 更新用户信息
            user.setUpdateTime(LocalDateTime.now());
            userMapper.updateByPrimaryKeySelective(user);
            log.info("==> 更新用户信息成功，user:{}",user);
        }
        return ResponseResult.okResult();
    }
    @Override
    public ResponseResult<?> getUserInfo() {
        // 获取当前登录用户的 ID
        Long userId = LoginUserContextHolder.getUserId();
        if (Objects.nonNull(userId)) {
            // 根据用户 ID 查询用户信息
            User user = userMapper.selectById(userId);
            UserInfoVO userInfoVO = UserInfoVO.builder()
                    .yantiId(user.getYantiId())
                    .nickname(user.getNickname())
                    .avatar(user.getAvatar())
                    .birthday(user.getBirthday())
                    .backgroundImg(user.getBackgroundImg())
                    .phone(user.getPhone())
                    .sex(user.getSex())
                    .introduction(user.getIntroduction())
                    .build();
            // 也可以使用Bean拷贝
            UserInfoVO userInfoVO1 = BeanUtil.toBean(user, UserInfoVO.class);
            log.info("==> 获取用户信息成功，userInfoVO1:{}",userInfoVO1);
            // 使用工具类进行拷贝
            UserInfoVO userInfoVO2 = BeanCopyUtils.copyBean(user, UserInfoVO.class);
            log.info("==> 获取用户信息成功，userInfoVO2:{}",userInfoVO2);
            // 构建返回结果
            return ResponseResult.okResult(userInfoVO);
        }
        log.info("==> 获取用户信息失败，userId:{}",userId);
        return ResponseResult.errorResult(AppHttpCodeEnum.USER_NOT_EXIST);
    }

    // 通用文件上传方法
    public String uploadFile(MultipartFile file, String fileType) {
        if (file.isEmpty()) {
            throw new BizException(AppHttpCodeEnum.AVATAR_UPLOAD_NOT_EMPTY);
        }
        try {
            return fileStrategy.uploadFile(file, BUCKET_NAME);
        } catch (Exception e) {
            throw new BizException(AppHttpCodeEnum.BACKGROUND_UPLOAD_NOT_EMPTY);
        }
    }

    /**
     * 系统自动注册用户
     * @param phone 手机号
     * @return userId 用户ID
     */
    public Long register(String phone){
        // 编程式事务
        return transactionTemplate.execute(status -> {
            try {
                // 获取全局自增的研题 ID
                Long yanTiId = redisTemplate.opsForValue().increment(RedisConstants.YANTI_ID_GENERATOR_KEY);
                // 构建用户
                User user = User.builder()
                        // 手机号
                        .phone(phone)
                        // 研题 ID
                        .yantiId(String.valueOf(yanTiId))
                        // 昵称
                        .nickname("研题用户"+yanTiId)
                        // 用户状态，是否可用
                        .status(StatusEnum.ENABLE.getValue())
                        // 创建时间
                        .createTime(LocalDateTime.now())
                        // 更新时间
                        .updateTime(LocalDateTime.now())
                        // 逻辑删除标记
                        .isDeleted(DeletedEnum.NO.getValue())
                        .build();

                // 保存用户到数据库
                userMapper.insert(user);
                // 手动使事务失效
                // 获取刚刚保存的用户的 ID
                Long userId = user.getId();
                // 给该用户分配一个默认角色
                UserRoleRel userRole =  UserRoleRel.builder()
                        // 关联的用户 ID
                        .userId(userId)
                        // 关联的角色 ID
                        .roleId(RoleConstants.COMMON_USER_ROLE_ID)
                        // 创建时间
                        .createTime(LocalDateTime.now())
                        // 更新时间
                        .updateTime(LocalDateTime.now())
                        // 逻辑删除标记
                        .isDeleted(DeletedEnum.NO.getValue())
                        .build();
                // 保存用户角色关系到数据库
                userRoleRelmapper.insert(userRole);
                // 构建用户角色数据
                ArrayList<Long> roles = Lists.newArrayList();
                roles.add(RoleConstants.COMMON_USER_ROLE_ID);
                // 构建用户角色数据 KEY
                String userRolesKey = RedisConstants.buildUserRoleKey(phone);
                // 将用户角色数据存入 Redis
                redisTemplate.opsForValue().set(userRolesKey, JSONUtil.toJsonStr(roles));
                return userId;
            }catch (Exception e){
                status.setRollbackOnly();// 标记事务为回滚
                log.error("==> 系统注册用户异常:", e);
                return null;
            }
        });
    }
}
